/*
 *  Chord.cpp
 *  SoundController0002
 *
 *  Created by jiro on 11/07/16.
 *  Copyright 2011 jiro music. All rights reserved.
 *
 */

#include "Theory0039_Chord.h"
#include "Theory0039_SingleGroupGenerator.h"
#include "../../sc/InvalidParameterException.h"
#include <sstream>

namespace theory0039 {

	Chord::Chord()
	{
	}

	Chord::Chord( sc::Note::Id note )
	{
		notes.push_back( note );
	}

	Chord::Chord( sc::Note::Id note1, sc::Note::Id note2 )
	{
		notes.push_back( note1 );
		notes.push_back( note2 );
	}

	Chord::Chord( sc::Note::Id note1, sc::Note::Id note2, sc::Note::Id note3 )
	{
		notes.push_back( note1 );
		notes.push_back( note2 );
		notes.push_back( note3 );
	}

	Chord::Chord( sc::Note::Id note1, sc::Note::Id note2, sc::Note::Id note3, sc::Note::Id note4 )
	{
		notes.push_back( note1 );
		notes.push_back( note2 );
		notes.push_back( note3 );
		notes.push_back( note4 );
	}

	Chord::Chord( sc::Note::Id note1, sc::Note::Id note2, sc::Note::Id note3, sc::Note::Id note4, sc::Note::Id note5 )
	{
		notes.push_back( note1 );
		notes.push_back( note2 );
		notes.push_back( note3 );
		notes.push_back( note4 );
		notes.push_back( note5 );
	}

	bool Chord::operator == ( const Chord& rhs ) const
	{
		bool isEqual = true;
		for ( int i = 0; i < GetNoteCount(); i++ )
		{
			bool areNotesEqual = false;
			for ( int j = 0; j < rhs.GetNoteCount(); j++ )
			{
				sc::Note::Id noteId = GetNoteIdOfIndex( i );
				sc::Note::Id noteIdRhs = rhs.GetNoteIdOfIndex( j );
				if ( noteId == noteIdRhs )
				{
					areNotesEqual = true;
					break;
				}
			}
			if ( not areNotesEqual )
			{
				isEqual = false;
				break;
			}
		}
		return isEqual;
	}

	bool Chord::operator != ( const Chord& rhs ) const
	{
		return not ( *this == rhs );
	}

	void Chord::AddNoteId( sc::Note::Id note )
	{
		notes.push_back( note );
		for ( int i = 0; i < GetIntervalCount(); i++ )
		{
			//	regard minus interval as plus interval
			intervalPattern.insert( abs( GetIntervalOfIndex( i ) ) );
		}
	}

	void Chord::AddGenerator( const SingleGroupGenerator& generator, const sc::Note::Id startNote )
	{
		generators.push_back( generator );
		generatorStartNotes.push_back( startNote );
	}

	int Chord::GetNoteCount() const
	{
		return notes.size();
	}
	
	sc::Note::Id Chord::GetNoteIdOfIndex( int index ) const
	{
		return notes[ index ];
	}

	int Chord::GetIntervalCount() const
	{
		//	add one for perfect 1st
		return 1 + ( GetNoteCount() * ( GetNoteCount() - 1 ) ) / 2;
	}

	sc::Note::Interval Chord::GetIntervalOfIndex( int index ) const
	{
		if ( index > GetIntervalCount() )
		{
			throw sc::InvalidParameterException( "sc::Note::Interval Chord::GetIntervalOfIndex( int index ) const" );
		}

		int index0;
		int index1;
		GetNoteIndeciesOfIntervalIndex( index, index0, index1 );

		int interval = sc::Note::GetInterval( notes[ index0 ], notes[ index1 ] );
		if ( interval > 6 )
		{
			interval -= 12;
		}
		return static_cast< sc::Note::Interval >( interval );
	}

	void Chord::GetNoteIdsOfIntervalIndex( int intervalIndex, sc::Note::Id& note0, sc::Note::Id& note1 ) const
	{
		int index0;
		int index1;
		GetNoteIndeciesOfIntervalIndex( intervalIndex, index0, index1 );
		note0 = notes[ index0 ];
		note1 = notes[ index1 ];
	}

	bool Chord::IsIncludeNote( sc::Note::Id note ) const
	{
		for ( int i = 0; i < GetNoteCount(); i++ )
		{
			if ( notes[ i ] == note )
			{
				return true;
			}
		}
		return false;
	}

	bool Chord::IsIncludeInterval( sc::Note::Interval interval ) const
	{
		int intervalCount = GetIntervalCount();
		for ( int i = 0; i < intervalCount; i++ )
		{
			if ( GetIntervalOfIndex( i ) == interval )
			{
				return true;
			}
		}
		return false;
	}

	void Chord::GetNoteIndeciesOfIntervalIndex( int intervalIndex, int& index0, int& index1 ) const
	{
		if ( intervalIndex == 0 )
		{
			index0 = 0;
			index1 = 0;
			return;
		}
		intervalIndex--;

		index0 = 0;
		index1 = 1;

		int index1Count = GetNoteCount() - 1;
		int index1Min = 1;
		while ( true )
		{
			if ( intervalIndex < index1Count )
			{
				index1 = index1Min + intervalIndex;
				break;
			}
			else
			{
				index0++;
				intervalIndex -= index1Count;
				index1Count--;
				index1Min++;
			}
		}
	}

	int Chord::GetGeneratorCount() const
	{
		return generators.size();
	}

	const SingleGroupGenerator& Chord::GetGeneratorOfIndex( int index ) const
	{
		return generators[ index ];
	}

	sc::Note::Id Chord::GetGeneratorStartNoteIdOfIndex( int index ) const
	{
		return generatorStartNotes[ index ];
	}
	
	const std::set< int >& Chord::GetIntervalPattern() const
	{
		return intervalPattern;
	}

	bool Chord::AreAllIntervalsSame( const Chord& chord ) const
	{
		int intervalCountThis = GetIntervalCount();
		int intervalCount = chord.GetIntervalCount();
		for ( int i = 0; i < intervalCountThis; i++ )
		{
			sc::Note::Interval intervalThis = GetIntervalOfIndex( i );
			bool isEqual = false;
			for ( int j = 0; j < intervalCount; j++ )
			{
				if ( intervalThis == chord.GetIntervalOfIndex( j ) )
				{
					isEqual = true;
					break;
				}
			}
			if ( not isEqual )
			{
				return false;
			}
		}
		return true;
	}

	std::string Chord::ToString() const
	{
		std::stringstream ss;
		for ( int i = 0; i < notes.size(); i++ )
		{
			ss << sc::Note::ToString( notes[ i ] ) << " ";
		}
		return ss.str();
	}

}
